using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using MoEngine;

namespace MoGameLib
{
    public enum BoardLayer : int
    {
        Terrain,
        River,
        Road,
        Overlay,
        Sites,
        Objects,
        Max
    };

    public delegate void TileDrawer(Tile tile, SpriteBatch spriteBatch, Vector2 cen);

    public struct LayerInfo
    {
        public bool Visable;
        public bool DelayDrawing;
        public TileDrawer DrawFunc;
    };
    /// <summary>
    /// A Hex map board, could generalize it later
    /// </summary>
    public class Board : IBoard
    {
        protected Map m_Map;

        protected LayerInfo[] m_LayerInfos;

        protected Vector2 TileSize = new Vector2(72, 84);

        protected SpriteBatch m_SpriteBatch;

        protected Texture2D m_ImageTerrain;
        protected Texture2D m_ImageFrame;

        public Board(Map map)
        {
            m_Map = map;

            // create graphics content
            m_SpriteBatch = new SpriteBatch(Core.GlobalDevice);

            // load content
            m_ImageFrame = Core.Content.Load<Texture2D>("Images/GUI/TileFrame");
            //m_ImageTerrain = Core.Content.Load<Texture2D>("Images/Terrain/Hill");

            // layer infos
            m_LayerInfos = new LayerInfo[(int)BoardLayer.Max];
            m_LayerInfos[(int)BoardLayer.Terrain].DrawFunc = DrawTerrain;
            m_LayerInfos[(int)BoardLayer.Overlay].DrawFunc = DrawOverlay;
            m_LayerInfos[(int)BoardLayer.Sites].DrawFunc = DrawSites;
        }

        public void DrawTerrain(Tile tile, SpriteBatch spriteBatch, Vector2 cen)
        {
            Texture2D imageTerrain = tile.Terrain.Image;
            spriteBatch.Draw(imageTerrain, cen - new Vector2(imageTerrain.Width / 2, imageTerrain.Height / 2), Color.White);
        }

        public void DrawOverlay(Tile tile, SpriteBatch spriteBatch, Vector2 cen)
        {
            spriteBatch.Draw(m_ImageFrame, cen - new Vector2(m_ImageFrame.Width / 2, m_ImageFrame.Height / 2), Color.White);
        }

        public void DrawSites(Tile tile, SpriteBatch spriteBatch, Vector2 cen)
        {
            if (tile.Sites.Count > 0)
            {
                Texture2D imageSite = tile.Sites[0].Sprite;
                spriteBatch.Draw(imageSite, cen - new Vector2(imageSite.Width / 2, imageSite.Height / 2), Color.White);
            }
        }

        protected virtual void DrawTile(Tile tile, int layer, SpriteBatch spriteBatch, Vector2 cen)
        {
            if (m_LayerInfos[layer].DrawFunc != null)
            {
                m_LayerInfos[layer].DrawFunc(tile, spriteBatch, cen);
            }
            else
            {
                return;
            }
        }

        #region IBoard Members

        public virtual void Render(ICamera camera)
        {
            // determine drawing range
            Point leftTop = camera.TopLeft;
            int left = (int)camera.TopLeft.X;
            int top = (int)camera.TopLeft.Y;
            int minTileX = Math.Max(0, left / (int)TileSize.X - 1);
            int minTileY = Math.Max(0, top / (int)TileSize.Y - 1);
            int maxTileX = Math.Min(m_Map.MaxCellX, (int)(left + camera.ViewportSize.X) / (int)TileSize.X + 1);
            int maxTileY = Math.Min(m_Map.MaxCellY, (int)(top + camera.ViewportSize.Y) / (int)TileSize.Y + 1);

            if (minTileX > maxTileX || minTileY > maxTileY)
                return;

            // draw all layers
            //Array layerIndices = Enum.GetValues(typeof(BoardLayer));
            int maxLayer = (int)BoardLayer.Max - 1;
            int startLayer = 0;

            m_SpriteBatch.Begin(
                SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred,
                SaveStateMode.None);

            while (startLayer <= maxLayer)
            {
                // find the end layer
                int endLayer = startLayer + 1;

                while (endLayer <= maxLayer && !m_LayerInfos[endLayer].DelayDrawing)
                    endLayer++;

                // draw from startLayer to endLayer-1
                Vector2 curPos;

                for (int y = minTileY; y <= maxTileY; y++)
                    for (int x = minTileX; x <= maxTileX; x++)
                    {
                        curPos.X = (float)Math.Round(-left + TileSize.X * (x + 0.5f));
                        curPos.Y = (float)Math.Round(-top + TileSize.Y * (y + ((x % 2 == 1) ? 1f : 0.5f)));

                        Tile tile = m_Map.GetTile(x, y);

                        for (int layer = startLayer; layer < endLayer; layer++)
                            DrawTile(tile, layer, m_SpriteBatch, curPos);
                    }

                // set new startLayer
                startLayer = endLayer;
            }

            m_SpriteBatch.End();
        }

        public Point PickTile(ICamera camera, Vector2 cursorPos)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion

    }
}
